/***************************************************************
 *
 * (c) 2008 Markus Dittrich 
 *
 * This program is free software; you can redistribute it 
 * and/or modify it under the terms of the GNU General Public 
 * License Version 3 as published by the Free Software Foundation. 
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License Version 3 for more details.
 *
 * You should have received a copy of the GNU General Public 
 * License along with this program; if not, write to the Free 
 * Software Foundation, Inc., 59 Temple Place - Suite 330, 
 * Boston, MA 02111-1307, USA.
 *
 ****************************************************************/

/****************************************************************
 *
 * Utilities provide general routines and typedefs used 
 * by other parts of the code.
 *
 ***************************************************************/

#ifndef UTILITIES_H 
#define UTILITIES_H

/** boost includes */
#include <boost/shared_ptr.hpp>
#include <boost/tokenizer.hpp>
#include <boost/filesystem/path.hpp>

/** STL includes */
#include <algorithm>
#include <limits>
#include <list>
#include <numeric>
#include <vector>

/** Qt includes */
#include <QString>

/** stdlib include */
#include <cmath>

/* local includes */
#include "basic_definitions.h"
#include "data_object.h"


/* convenience typedefs */
typedef boost::char_separator<char> Separator;
typedef boost::tokenizer<Separator> Tokenizer;


/* floating point constants */
const double MAX_DOUBLE = std::numeric_limits<double>::max();
const double EPSILON_DOUBLE = std::numeric_limits<double>::epsilon();
const double SQRT_MAX_DOUBLE = sqrt(MAX_DOUBLE);



/****************************************************************
 * 
 * functions 
 *
 ***************************************************************/

//--------------------------------------------------------------
// splits a string into a vector of doubles; splitting is done
// at a given separator or space by default
//--------------------------------------------------------------
bool string_to_doubles(std::vector<double>& container,
  std::string& aString,
  int maxLength = std::numeric_limits<int>::max(),
  Separator aSeparator = Separator(" "));


//--------------------------------------------------------------
// given the x and y coordinate as doubles generate a string
// for display in the main window 
//--------------------------------------------------------------
QString generate_coordinate_string(double x, double y);


//--------------------------------------------------------------
// function testing if a particular x or y data column is set 
// to the number of elements
//--------------------------------------------------------------

/* x column */
bool has_element_count_as_x(const DataObjectPtr dataSet);

inline bool has_element_count_as_x(const DataObjectPtr dataSet)
{
  return (dataSet->get_active_x_column() == ELEMENT_COUNT);
}


/* y column */
bool has_element_count_as_y(const DataObjectPtr dataSet);

inline bool has_element_count_as_y(const DataObjectPtr dataSet)
{
  return (dataSet->get_active_y_column() == ELEMENT_COUNT);
}


//--------------------------------------------------------------
// strips the leading path from a unix filename and returns
// the plain filename
// use boost::filesystem::path to be as portable as possible
// even though the code is a bit clunky due to conversions
// between std::string and QString
//--------------------------------------------------------------
QString strip_path(const QString& path);

inline QString strip_path(const QString& aPath)
{
  boost::filesystem::path currentPath(aPath.toStdString());
  return QString(currentPath.leaf().c_str());
}


//--------------------------------------------------------------
// return histogram name based on name of a data set
//--------------------------------------------------------------
QString generate_histogram_name(const QString& dataName, int id);

inline QString generate_histogram_name(const QString& dataName,
    int id)
{
  return "hist:" + QString::number(id) + ":" 
    + strip_path(dataName);
}


//--------------------------------------------------------------
// formats a double value based on its precision and returns
// it as a QString
//--------------------------------------------------------------
QString pretty_format_double(double number);


/*****************************************************************
 * special min/max/mean/median functions needed for histogram
 * data sets since in this case we store the coordinate to
 * draw the bars rather than some raw data
 *****************************************************************/

//--------------------------------------------------------------
// convert histogram locations into vector of bar locations;
// here location is the center of a bar
//--------------------------------------------------------------
void extract_bin_locations(const std::vector<double>& histogramXData, 
    std::vector<double>& binSize);


//--------------------------------------------------------------
// convert histogram data into raw data set of bar heights
//--------------------------------------------------------------
void extract_bin_heights(const std::vector<double>& histogramYData, 
    std::vector<double>& binSize);


//--------------------------------------------------------------
// extract the bin width from the data set
//--------------------------------------------------------------
double extract_histogram_bin_width(
    const std::vector<double>& histogramData);


//--------------------------------------------------------------
// extract the total number of elements in all bins of the
// histogram
//--------------------------------------------------------------
int extract_total_bin_element_count(bool normalized,
    const std::vector<double>& histogramYData);


//--------------------------------------------------------------
// value of highest bar
//--------------------------------------------------------------
bool histogram_max(const std::vector<double>& binSize,
    double& histMax);


//--------------------------------------------------------------
// value of lowest bar
//--------------------------------------------------------------
bool histogram_min(const std::vector<double>& binSize,
    double& histMin);


//--------------------------------------------------------------
// mean value of bar heights
//--------------------------------------------------------------
bool histogram_mean(const std::vector<double>& binLocations,
    const std::vector<double>& binSize, 
    int numElements, double& histMean);


//--------------------------------------------------------------
// median value of bar heights
//--------------------------------------------------------------
bool histogram_median(const std::vector<double>& binLocations,
    const std::vector<double>& binSize, int numElements, 
    double& histMedian);



/*****************************************************************
 * return certain properties of a collection of items in a
 * container sequence specified by an iterator to the beginning
 * and end
 *****************************************************************/

//--------------------------------------------------------------
// max
//--------------------------------------------------------------
template<typename ForwardIterator>
bool data_max(ForwardIterator first, ForwardIterator last,
              double& dataMax)
{
  if ( first == last )
  {
    return false;
  }

  dataMax = *std::max_element(first, last);
  return true;
}


//--------------------------------------------------------------
// min
//--------------------------------------------------------------
template<typename ForwardIterator>
bool data_min(ForwardIterator first, ForwardIterator last,
              double& dataMin)
{
  if ( first == last)
  {
    return false;
  }
  
  dataMin = *std::min_element(first, last);
  return true;
}


//--------------------------------------------------------------
// mean
// NOTE: Our method of computation probably sucks big time
// once elements in the collection have widely varying sizes. 
//--------------------------------------------------------------
template<typename ForwardIterator>
bool data_mean(ForwardIterator first, ForwardIterator last,
               double& dataMean)
{
  if ( first == last )
  {
    return false;
  }

  double mean = std::accumulate(first, last, 0.0); 
  dataMean = mean /(last-first);

  return true;
}


//--------------------------------------------------------------
// median
// NOTE: Our method of computation probably sucks big time
// once elements in the collection have widely varying sizes. 
//--------------------------------------------------------------
template<typename ForwardIterator>
bool data_median(ForwardIterator first, ForwardIterator last,
               double& dataMedian)
{
  if ( first == last )
  {
    return false;
  }

  std::vector<typename ForwardIterator::value_type> 
    values(first, last);
  std::sort(values.begin(),values.end());

  /* if we have an odd number of elements we return the
   * middle element, otherwise the average of the two
   * central elements */
  int length = values.size();
  int middleElement = length/2;
  if ( (length & 1) == 0 )    // even
  {
    dataMedian = (values[middleElement]+values[middleElement+1])/2.0;
  }
  else                        // odd
  {
    dataMedian = values[middleElement];
  }

  return true;
}


//--------------------------------------------------------------
// variance
// NOTE: If any of the elements in the container is larger
// than sqrt(DBL_MAX) we abort since we'll likely fail in
// pow((mean-element,2))
//--------------------------------------------------------------
template<typename ForwardIterator>
bool data_variance(ForwardIterator first, ForwardIterator last,
                   double& dataVariance)
{
  if ( first == last )
  {
    return false;
  }

  if ( *first >= SQRT_MAX_DOUBLE )
  {
    return false;
  }

  double mean = 0;
  if ( !data_mean(first, last, mean) )
  {
    return false;
  }
  
  double variance = pow((mean - *first),2);
  int counter = 1;
  while ( ++first != last )
  {
    if ( *first >= SQRT_MAX_DOUBLE )
    {
      dataVariance = 0.0;
      return false;
    }

    variance += pow((mean - *first),2);
    ++counter;
  }

  /* divide by N-1 to yield an unbiased estimator */
  variance /= (counter-1);

  dataVariance = variance;
  return true;
}


//--------------------------------------------------------------
// standard deviation
//--------------------------------------------------------------
template<typename ForwardIterator>
bool data_stddev(ForwardIterator first, ForwardIterator last,
              double& dataStd)
{
  if ( first == last )
  {
    return false;
  }

  double variance = 0;
  if ( !data_variance(first, last, variance) )
  {
    return false;
  }

  if ( variance < 0 )
  {
    return false;
  }

  dataStd = sqrt(variance);
  return true;
}


#endif
